﻿using Microsoft.Extensions.Caching.Memory;
using System.Collections;
using System.Collections.Concurrent;
using System.Reflection;
using System.Runtime.CompilerServices;

namespace NetHelperCore
{
    public class CacheHelper
    {
        public static IMemoryCache Cache = new MemoryCache(new MemoryCacheOptions());

        /// <summary>
        /// 验证缓存项是否存在
        /// </summary>
        /// <param name="key">缓存Key</param>
        /// <returns></returns>
        public static bool Exists(string key)
        {

            if (string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentNullException(nameof(key));
            }
            return Cache.TryGetValue(key, out _);
        }

        /// <summary>
        /// 增加缓存
        /// </summary>
        /// <param name="key">缓存唯一标识</param>
        /// <param name="value">缓存的数据</param>
        /// <param name="absoluteTime">绝对时间释放缓存</param>
        /// <param name="slidingTime">滑动过期时长（如果在过期时间内有操作，则以当前时间点延长过期时间(秒)，否则自动释放），如果为0则不设置</param>
        public static void CacheAdd(string key, object value, DateTime? absoluteTime, int slidingTime, Action<string, object> removeCallBack = null)
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentNullException(nameof(key));
            }

            MemoryCacheEntryOptions _options = new MemoryCacheEntryOptions();
            if (slidingTime <= 0)
            {
                _options.AbsoluteExpiration = absoluteTime.Value;
            }
            else
            {
                _options.SlidingExpiration = TimeSpan.FromSeconds(slidingTime);
            }

            if (removeCallBack != null)
            {
                //EvictionReason 为从缓存中逐出条目的原因 None,项未从缓存中删除。Removed,手动。Replaced,覆盖。Expired,已超时。TokenExpired,事件。Capacity,溢出。
                _options.RegisterPostEvictionCallback((object cacheKey, object cacheValue, EvictionReason evictionReason, object state) =>
                {
                    removeCallBack(Convert.ToString(cacheKey), cacheValue);
                });
            }

            Cache.Set(key, value, _options);
        }



        /// <summary>
        /// 移除缓存
        /// </summary>
        /// <param name="key">缓存唯一标识</param>
        public static void CacheRemove(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentNullException(nameof(key));
            }
            Cache.Remove(key);
        }

        /// <summary>
        /// 获取缓存值
        /// </summary>
        /// <param name="key">缓存唯一标识</param> 
        /// <returns></returns>
        public static object CacheGet(string key)
        {
            if (string.IsNullOrWhiteSpace(key))
            {
                throw new ArgumentNullException(nameof(key));
            }
            object _obj = null;
            Cache.TryGetValue(key, out _obj);
            return _obj; 
        }



        /// <summary>
        /// 获取所有缓存键
        /// </summary>
        /// <returns></returns>
        public static List<string> GetCacheKeys()
        {
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
            var coherentState = Cache.GetType().GetField("_coherentState", flags).GetValue(Cache);//增加一个获取CoherentState对象环节
            var entries = coherentState.GetType().GetField("_entries", flags).GetValue(coherentState);
            var cacheItems = entries as IDictionary;
            var keys = new List<string>();
            if (cacheItems == null) return keys;
            foreach (DictionaryEntry cacheItem in cacheItems)
            {
                keys.Add(cacheItem.Key.ToString());
            }
            return keys;
        }

    }

    public class CacheListHelper
    {

        private static bool IsBeginCheckData = false;

        /// <summary>
        /// 缓存列表
        /// </summary>
        private static ConcurrentDictionary<string, CacheModel> CacheList = new ConcurrentDictionary<string, CacheModel>();


        /// <summary>
        /// 可自动调用更新函数的缓存
        /// </summary>
        /// <param name="key"></param>
        /// <param name="dataValidSecond"></param>
        /// <param name="initDataFunc"></param>
        /// <param name="initFuncParam"></param>
        /// <returns></returns>
        public static object CacheGet(string key, int dataValidSecond, Func<Dictionary<string, string>, object> initDataFunc, Dictionary<string, string> initFuncParam)
        {
            CheckDataTime();
            if (CacheList.ContainsKey(key))
            {
                return CacheList[key].CacheValue;
            }
            else
            {
                using (var mutex = new System.Threading.Mutex(false, "CacheInit_" + key))
                {
                    try
                    {
                        if (mutex.WaitOne(-1, false))
                        {
                            if (CacheList.ContainsKey(key))
                            {
                                return CacheList[key].CacheValue;
                            }

                            object _obj = initDataFunc(initFuncParam);
                            CacheList.TryAdd(key, new CacheModel
                            {
                                CacheValue = _obj,
                                DataValidSecond = dataValidSecond,
                                DataValidTime = DateTime.Now.AddSeconds(dataValidSecond),
                                InitDataFunc = initDataFunc,
                                InitFuncParam = initFuncParam
                            });
                            return _obj;
                        }

                    }
                    finally
                    {
                        mutex.ReleaseMutex();
                    }
                    return null;
                }


            }
        }

        /// <summary>
        /// 检查那些缓存已经过期
        /// </summary>
        [MethodImpl(MethodImplOptions.Synchronized)]
        public static void CheckDataTime()
        {
            if (!IsBeginCheckData)
            {
                IsBeginCheckData = true;
                Task.Factory.StartNew(() =>
                {
                    while (true)
                    {
                        for (int i = CacheList.Count - 1; i >= 0; i--)
                        {
                            var cache = CacheList.ElementAt(i);
                            if (cache.Value.DataValidTime < DateTime.Now)
                            {
                                Task.Factory.StartNew(() =>
                                {
                                    bool _isRunFunc = false;
                                    using (var mutex = new System.Threading.Mutex(true, "CacheInit_" + cache.Key, out _isRunFunc))
                                    {
                                        try
                                        {
                                            if (_isRunFunc)
                                            {

                                                object _obj = cache.Value.InitDataFunc(cache.Value.InitFuncParam);
                                                CacheModel _cache = new CacheModel();

                                                _cache.CacheValue = _obj;
                                                _cache.DataValidSecond = cache.Value.DataValidSecond;
                                                _cache.DataValidTime = DateTime.Now.AddSeconds(cache.Value.DataValidSecond);
                                                _cache.InitDataFunc = cache.Value.InitDataFunc;
                                                _cache.InitFuncParam = cache.Value.InitFuncParam;

                                                CacheList.AddOrUpdate(cache.Key, _cache, (k, v) => _cache);
                                            }

                                        }
                                        finally
                                        {

                                        }
                                    }
                                });

                            }
                        }
                        Thread.Sleep(1000);
                    }
                });
            }
        }
    }

    /// <summary>
    /// 缓存数据
    /// </summary>
    public class CacheModel
    {
        /// <summary>
        /// 缓存值
        /// </summary>
        public object CacheValue { get; set; } = null;

        /// <summary>
        /// 数据有效描述
        /// </summary>
        public int DataValidSecond { get; set; } = 0;

        /// <summary>
        /// 数据有效时间，过期后自动调用更新函数
        /// </summary>
        public DateTime DataValidTime { get; set; } = DateTime.MinValue;

        /// <summary>
        /// 更新缓存函数
        /// </summary>
        public Func<Dictionary<string, string>, object> InitDataFunc { get; set; }

        /// <summary>
        /// 更新缓存函数参数
        /// </summary>
        public Dictionary<string, string> InitFuncParam { get; set; }
    }


}
